package com.jackykeke.opengldemo.renderer;

import android.opengl.GLES30;
import android.opengl.GLSurfaceView;

import com.jackykeke.opengldemo.R;
import com.jackykeke.opengldemo.utils.ResReadUtil;
import com.jackykeke.opengldemo.utils.ShaderUtil;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * 顶点缓冲区对象(Vertex Buffer Object)，简称VBO。如果不使用顶点缓冲区对象(VBO)则是将顶点、颜色、纹理坐标等数据存放在内存(客户内存)当中，
 * 在每次进行glDrawArxays或者gIDrawElements等绘图调用时，必须从客户内存复制到图形内存。
 * 而顶点缓冲区对象使OpenGL ES 3.0应用程序可以在高性能的图形内存中分配和缓存顶点数据，并从这个内存进行渲染，
 * 从而避免在每次绘制图元的时候重新发送数据。不仅是顶点数据，描述图元顶点索引、作为glDrawElements参数传递的元素索引也可以缓存。
 * <p>
 * OpenGL ES 3.0支持两类缓冲区对象：数组缓冲区对象和元素数组缓冲区对象。
 * <p>
 * GL_ARRAY_BUFFER标志指定的数组缓冲区对象用于创建保存顶点数据的缓冲区对象。
 * GL_ELEMENT_ARRAY_BUFFER标志指定的元素数组缓冲区对象用于创建保存图元索引的缓冲区对象。
 * OpenGL ES 3.0中的其他缓冲区对象类型：统一变量缓冲区、变换反馈缓冲区、像素解包缓冲区、 像素包装缓冲区和复制缓冲区。
 * <p>
 * OpenGL ES 3.0建议应用程序对顶点属性数据和元素索引使用顶点缓冲区对象。
 */


public class VertexBufferObjectRenderer implements GLSurfaceView.Renderer {


    private static final String TAG = VertexBufferObjectRenderer.class.getSimpleName();

    private final FloatBuffer vertexBuffer;

    private int mProgram;


    private static final int VERTEX_POS_INDEX = 0;


    private static final int VERTEX_POS_SIZE = 3;

    private static final int VERTEX_STRIDE = VERTEX_POS_SIZE * 4;

    /**
     * 缓冲数组
     */
    private int[] vboIds = new int[1];

    /**
     * 点的坐标
     */
    private float[] vertexPoint = new float[]{
            0.0f, 0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f
    };


    public VertexBufferObjectRenderer() {
        //分配内存空间,每个浮点型占4字节空间
        vertexBuffer = ByteBuffer.allocateDirect(vertexPoint.length * 4)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();
        vertexBuffer.put(vertexPoint);
        vertexBuffer.position(0);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {

        //设置背景颜色
        GLES30.glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
        //编译
        final int vertexShaderId = ShaderUtil.compileVertexShader(ResReadUtil.readResource(R.raw.vertex_buffer_shader));
        final int fragmentShaderId = ShaderUtil.compileFragmentShader(ResReadUtil.readResource(R.raw.fragment_buffer_shader));
        //鏈接程序片段
        mProgram = ShaderUtil.linkProgram(vertexShaderId, fragmentShaderId);

        // //1. 生成1个缓冲ID
        GLES30.glGenBuffers(1, vboIds, 0);
        //2. 绑定到顶点坐标数据缓冲
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboIds[0]);
        //3. 向顶点坐标数据缓冲送入数据
        GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, vertexPoint.length * 4, vertexBuffer, GLES30.GL_STATIC_DRAW);
        //4. 将顶点位置数据送入渲染管线
        GLES30.glVertexAttribPointer(VERTEX_POS_INDEX, VERTEX_POS_SIZE, GLES30.GL_FLOAT, false, VERTEX_STRIDE, 0);
        //5. 启用顶点位置属性
        GLES30.glEnableVertexAttribArray(VERTEX_POS_INDEX);

    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES30.glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {

        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
        //使用程序片段
        GLES30.glUseProgram(mProgram);

        //开始绘制三角形
        GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 3);

        //解绑VBO
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
    }
}
